package com.inspinia.base.util;

import com.inspinia.base.common.exception.BusinessException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ResourceUtils;
import sun.misc.BASE64Encoder;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.nio.file.Files;
import java.util.Date;
import java.util.List;

/**
 * Created by veblen on 15-10-13.
 */
public class FileUtil {

    /**
     * 日志对象
     */
    private static Logger logger = LoggerFactory.getLogger(FileUtil.class);

    /**
     * 文件随机名称，使用PathFormat.parse()格式化
     */
    public static final String RANDOM_FILENAME = "{yyyy}{mm}{dd}/{time}{rand:6}";

    /**
     * 随机的文件名称,没有年月日
     */
    public static final String RANDOM_TIME_FILENAME = "{time}{rand:6}";

    /**
     * 文件原始名和随机名的分隔符
     */
    public static final String ORIGIN_FILENAME_SEPARATOR = "_";


    /**
     * 生成文书文件的文件夹
     */
    public static final String DOC_FILE_MODULE_NAME = "assist-doc";

    /**
     * 资金流树导出的临时文件夹
     */
    public static final String EXPORT_TEMP = "export-temp";
    /**
     * 文件后缀的时间格式化字符串
     */
    public static final String FILE_SUFFIX_TIME_PATTERN = "yyyyMMddHHmmss";


    /**
     * 获得随机文件名称
     *
     * @return
     */
    public static String randomFilename() {
        return PathFormat.parse(RANDOM_FILENAME);
    }

    /**
     * 获得随机文件名称
     *
     * @return
     */
    public static String randomTimeFilename() {
        return PathFormat.parse(RANDOM_TIME_FILENAME);
    }


    /**
     * 获取模板文件的绝对路径
     *
     * @param fileName
     * @return
     */
    public static String getResourcesAbsolutePath(String folder, String fileName) {
        try {
            File file = ResourceUtils.getFile("classpath:" + (StringUtils.isNotBlank(folder) ? folder + "/" : "") + fileName);
            return file.getAbsolutePath();
        } catch (FileNotFoundException e) {
            logger.error("", e);
            return null;
        }
    }

    /**
     * 获得文件后缀名
     *
     * @param filename 文件名称
     */
    public static String getSuffix(String filename) {
        if (filename == null) {
            return null;
        }
        int index = filename.lastIndexOf(".");
        return index >= 0 ? filename.substring(index + 1) : "";
    }

    /**
     * 获得文件的绝对路径
     *
     * @param path 相对路径
     */
    public static String getAbsolutePath(String folder, String path) {
        return new File(folder + "/" + path).getAbsolutePath();
    }

    /**
     * 获取上传文件根目录URL
     *
     * @param servletPath 相对路径
     * @param req
     */
    public static String getUploadUrl(String servletPath, HttpServletRequest req) {
        if (StringUtils.isEmpty(servletPath)) {
            return servletPath;
        }

        return StringUtils.getRootPath(req) + "/upload" + servletPath;
    }

    /**
     * 获取下载URL根路径
     *
     * @param req
     */
    public static String getDownloadRootUrl(HttpServletRequest req) {
        return StringUtils.getRootPath(req) + "/download";
    }


    /**
     * 把byte数组的内容写入文件
     *
     * @param moduleName 文件绝对路径
     * @param data       文件内容
     * @return 保存在服务器文件夹的文件名。
     */
    public static String upload(String moduleName, byte[] data, String originalFilename, String uploadPath) throws IOException {
        String fileName = genRandomFileName(moduleName, originalFilename);
        String filePath = uploadPath + "/" + fileName;
        ByteArrayInputStream is = null;
        FileOutputStream out = null;
        try {

            is = new ByteArrayInputStream(data);
            if (is != null) {
                File file = create(filePath);
                out = new FileOutputStream(file);
                byte[] buffer = new byte[4 * 1024];
                int len = 0;
                while ((len = is.read(buffer)) != -1) {
                    out.write(buffer, 0, len);
                }
                out.flush();
            }
            return fileName;
        } finally {
            IOUtils.closeQuietly(is);
            IOUtils.closeQuietly(out);
        }
    }

    /**
     * 把stream写入到文件
     *
     * @param moduleName       文件绝对路径
     * @param input            输入流
     * @param originalFilename
     * @return 保存在服务器文件夹的文件名。
     */
    public static String upload(String moduleName, InputStream input, String originalFilename, String uploadPath) throws IOException {
        String fileName = genRandomFileName(moduleName, originalFilename);
        String filePath = uploadPath + File.separator + fileName;
        File dir = new File(filePath).getParentFile();
        dir.mkdirs();
        FileOutputStream output = new FileOutputStream(filePath);
        try {
            IOUtils.copy(input, output);
            output.flush();
            return fileName;
        } finally {
            IOUtils.closeQuietly(output);
        }
    }

    /**
     * 把stream写入到文件
     *
     * @param moduleName 文件绝对路径
     * @param input      输入流
     * @param filename   原始文件名
     * @return 保存在服务器文件夹的文件名。
     */
    public static String uploadFile(String moduleName, InputStream input, String filename, String uploadPath) throws IOException {
        String filePath = uploadPath + File.separator + moduleName + File.separator + filename;
        File dir = new File(filePath).getParentFile();
        dir.mkdirs();
        FileOutputStream output = new FileOutputStream(filename);
        try {
            IOUtils.copy(input, output);
            output.flush();
            return filename;
        } finally {
            IOUtils.closeQuietly(output);
        }
    }

    /**
     * 判断文件是否存在
     *
     * @param fileFullPath 文件全路径
     * @return true表示文件存在
     */
    public static boolean isExists(String fileFullPath) {
        return new File(fileFullPath).exists();
    }

    /**
     * 创建文件
     *
     * @param fileFullPath 文件全路径
     */
    public static File create(String fileFullPath) {
        File file = new File(fileFullPath);
        if (!file.exists()) {
            try {
                file.getParentFile().mkdirs();
                file.createNewFile();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return file;
    }

    /**
     * 把byte数组的内容写入文件
     *
     * @param folder 文件夹
     * @param data   文件内容
     * @return 保存在服务器文件夹的文件名。
     */
    public static String upload(String folder, String fileName, byte[] data) throws IOException {
        if (data != null) {
            ByteArrayInputStream is = null;
            FileOutputStream out = null;
            String randomFileName = null;
            try {

                is = new ByteArrayInputStream(data);
                if (is != null) {
                    randomFileName = genRandomFileName(null, fileName);
                    File file = create(folder + "/" + randomFileName);
                    out = new FileOutputStream(file);
                    byte[] buffer = new byte[4 * 1024];
                    int len = 0;
                    while ((len = is.read(buffer)) != -1) {
                        out.write(buffer, 0, len);
                    }
                    out.flush();
                }
            } finally {
                IOUtils.closeQuietly(is);
                IOUtils.closeQuietly(out);
            }
            return randomFileName;
        }
        return null;
    }

    /**
     * 把stream写入到文件
     *
     * @param moduleName       文件绝对路径
     * @param input            输入流
     * @param originalFilename 文件原始名称
     * @param folder           指定的文件夹
     * @return 保存在服务器文件夹的文件名。
     */
    public static String upload(String moduleName, InputStream input, String originalFilename, String folder, String uploadPath) throws IOException {
        String fileName = genRandomFileName(moduleName, originalFilename);
        String filePath;
        if (StringUtils.isNotEmpty(folder)) {
            filePath = folder + "/" + fileName;
        } else {
            filePath = uploadPath + "/" + fileName;
        }
        File dir = new File(filePath).getParentFile();
        dir.mkdirs();
        FileOutputStream output = new FileOutputStream(filePath);
        try {
            IOUtils.copy(input, output);
            output.flush();
            return fileName;
        } finally {
            IOUtils.closeQuietly(output);
        }
    }

    /**
     * 生成随机文件名
     *
     * @param moduleName       模块名
     * @param originalFilename 文件原始名
     * @return
     */
    public static String genRandomFileName(String moduleName, String originalFilename) {

        String fileName = randomFilename() + ORIGIN_FILENAME_SEPARATOR + StringEscapeUtils.unescapeHtml4(originalFilename);
        if (StringUtils.isNotEmpty(moduleName)) {
            return moduleName + "/" + fileName; //最终写入服务器文件夹的文件名。
        }
        return fileName;
    }


    /**
     * 查找ftp的文件
     *
     * @param filePath
     * @param ftp
     * @param files
     * @return
     * @throws IOException
     */
    public static List<FTPFile> listFTPFiles(String filePath, FTPClient ftp, List<FTPFile> files) throws IOException {
        FTPFile[] ftpFiles = ftp.listFiles(filePath);
        for (FTPFile file : ftpFiles) {
            if (file.isDirectory()) {
                listFTPFiles(file.getName(), ftp, files);
            }
            files.add(file);
        }
        return files;
    }

    /**
     * 得到文件的原始文件名
     *
     * @param fileName 文件名,格式应当类似:test/20160823/1471920760354019015_新建文本文档.txt,最后一个下划线后面的为原始的文件名。
     * @return 文件原始文件名
     */
    public static String getOriginFileName(String fileName) {
        return fileName.substring(fileName.lastIndexOf(ORIGIN_FILENAME_SEPARATOR) + 1, fileName.length());
    }

    /**
     * 获取下载文件的文件名
     *
     * @param fileName
     * @param needSysSuffix true表示需要后缀，false表示不要要后缀。解决某些情况不需要加系统后缀的的情况,默认是true
     * @return
     */
    public static String getDownloadFileName(String fileName, Boolean needSysSuffix, String downloadFileSuffixType) {
        String originFileName = getOriginFileName(fileName);
        if (needSysSuffix == null || needSysSuffix) {
            if (StringUtils.isNotBlank(downloadFileSuffixType) && downloadFileSuffixType.equals("time")) {
                String suffix = "." + getSuffix(originFileName);
                String format = DateFormatUtils.format(new Date(), FILE_SUFFIX_TIME_PATTERN);
                originFileName = originFileName.replace(suffix, "-" + format + suffix);
            }
        }
        return originFileName;
    }

    /**
     * 获取下载文件的文件名
     *
     * @param fileName
     * @param needSysSuffix true表示需要后缀，false表示不要要后缀。解决某些情况不需要加系统后缀的的情况,默认是true
     * @return
     */
    public static String getDownloadFileName(String fileName, Boolean needSysSuffix, String fileUser, String caseAssistOrgType, String downloadFileSuffixType) {
        String originFileName = getOriginFileName(fileName);
        String type = "";
        switch (caseAssistOrgType) {
            case "1":
                type = "银行协作";
                break;
            case "2":
                type = "银行协作";
                break;
            case "3":
                type = "银行协作";
                break;
            case "4":
                type = "第三方协作";
                break;
            case "5":
                type = "运营商协作";
                break;
            case "6":
                type = "三侦协作";
                break;
        }
        String suffix = "." + getSuffix(originFileName);
        if (StringUtils.isNotBlank(downloadFileSuffixType) && downloadFileSuffixType.equals("time")) {
            String format = DateFormatUtils.format(new Date(), FILE_SUFFIX_TIME_PATTERN);
            originFileName = fileUser + "_" + type + "_" + format;
        }
        if (needSysSuffix != null && needSysSuffix) {
            originFileName = originFileName + suffix;
        }
        return originFileName;
    }

    /**
     * 删除文件
     *
     * @param fileName 文件名
     * @param folder   文件夹,默认使用配置的上传文件夹
     * @return
     */

    public static boolean deleteFile(String fileName, String folder) throws IOException, BusinessException {
        String absolutePath = getAbsolutePath(folder, fileName);
        File file = new File(absolutePath);
        if (!file.exists()) {
            throw new BusinessException("文件不存在");
        }
        FileUtils.forceDelete(file);
        return true;
    }

    /**
     * 删除多个文件
     */
    public static void deleteFiles(String[] filePathArr, String uploadPath) {
        for (int i = 0; i < filePathArr.length; i++) {
            String fileName = filePathArr[i];//取上传后的文件的名字 
            try {
                fileName = URLDecoder.decode(fileName, "utf-8");
                FileUtil.deleteFile(fileName, uploadPath);
            } catch (UnsupportedEncodingException e) {
                logger.error(e.getMessage());
                e.printStackTrace();
            } catch (IOException e) {
                logger.error(e.getMessage());
                e.printStackTrace();
            }
        }
    }


    /**
     * 将一个 用逗号为分隔符的字符串的路劲 分成
     */


    /**
     * 打包多文件为zip压缩包
     *
     * @param files      文件
     * @param zipPath    压缩包文件路径
     * @param uploadPath
     * @return
     */
    public static File batchFile2Zip(File[] files, String zipPath, String uploadPath) {
        //对zipPath名字操作，为空则设置默认名
        if (StringUtils.isEmpty(zipPath)) {
            zipPath = genRandomFileName("", "批量文件.zip");
        } else {
            if (!zipPath.contains(".")) {
                zipPath += ".zip";
            }
            ;
        }
        File zipFile = new File(uploadPath + File.separator + zipPath);
        File dir = zipFile.getParentFile();
        try {
            if (!dir.exists()) {
                dir.mkdirs();
            }
            ZipFilesUtil.ZipFiles(zipFile, "", files);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return zipFile;
    }

    /**
     * 将文件转换成Base64编码
     * 注意:文件过大会堆内存溢出
     *
     * @param filePath 文件路径
     * @return 编码字符串
     * @throws IOException
     */
    public static String file2Base64(String filePath) {
        byte[] data = null;
        InputStream in = null;
        try {
            in = new FileInputStream(filePath);
            data = new byte[in.available()];
            in.read(data);
            in.close();
            /** 进行base64位编码 **/
            BASE64Encoder encoder = new BASE64Encoder();
            return encoder.encode(data);
        } catch (Exception e) {
            logger.error("", e);
            return null;
        }

    }

    /**
     * 文所在的文件夹如果不存在则生成文件夹
     *
     * @param outPath 文件路径
     */
    public static void createFolder(String outPath) {
        File folder = new File(new File(outPath).getParent());
        if (!folder.exists()) {
            folder.mkdirs();
        }
    }

    /**
     * 判断字符串是否是base64编码
     *
     * @param str 被判断的字符串
     * @return
     */
    public static boolean isBase64(String str) {
        try {
            Class clazz = Class.forName("com.sun.org.apache.xerces.internal.impl.dv.util.Base64");
            Method mainMethod = clazz.getMethod("decode", String.class);
            mainMethod.setAccessible(true);
            Object retObj = mainMethod.invoke(null, str);
            return !(retObj == null);
        } catch (Exception e) {
            logger.error("", e);
            return false;
        }
    }

    /**
     * 将xml文档转换为docx
     * 只支持微软open xml标准的文档(office另存为xml文件就是open xml标准的)
     *
     * @param xmlPath  要转换的xml文件路径
     * @param docxPath 生成的docx文件的路径
     * @return true:成功  false:失败
     */
    public static boolean xml2docx(String xmlPath, String docxPath) {
        WordprocessingMLPackage mlPackage = null;
        try {
            mlPackage = WordprocessingMLPackage.load(new File(xmlPath));
            mlPackage.save(new File(docxPath));
            return true;
        } catch (Docx4JException e) {
            logger.error("xml文档转换为docx文档时出错", e);
            return false;
        }
    }

    //写文件的buffer的大小
    public static final int OUT_FILE_BUFFER_SIZE = 2048;

    /**
     * 写入文件
     *
     * @param inputStream 输入流
     * @param filePath    文件路径
     * @return
     * @throws IOException
     */
    public static void write2file(InputStream inputStream, String filePath) throws IOException {
        File file = FileUtil.create(filePath);
        byte[] buffer = new byte[OUT_FILE_BUFFER_SIZE];
        int len;
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
            while ((len = inputStream.read(buffer)) > 0) {
                fos.write(buffer, 0, len);
            }
        } finally {
            IOUtils.closeQuietly(fos);
            IOUtils.closeQuietly(inputStream);
        }
    }


    /**
     * 判断文件是否存在
     *
     * @return
     */
    public static boolean isExist(String filePath) {
        if (StringUtils.isNotEmpty(filePath)) {
            File file = new File(filePath);
            return file.exists();
        }
        return false;
    }

    public static boolean isExist(String folder, String fileName) {
        if (StringUtils.isNotEmpty(folder) && StringUtils.isNotEmpty(fileName)) {
            File file = new File(folder, fileName);
            return file.exists();
        }
        return false;
    }

    /**
     * 合并文件
     * 默认是从文件的末尾开始添加
     *
     * @param destFile  目标文件
     * @param tempFiles 临时文件
     */
    public static void appendFile(File destFile, File... tempFiles) throws IOException {
        if (!destFile.exists()) {
            destFile.createNewFile();
        }
        int len;
        byte[] buffer = new byte[1024];
        RandomAccessFile raf = new RandomAccessFile(destFile.getAbsolutePath(), "rw");
        FileInputStream fis = null;
        long start = destFile.length();
        try {
            for (File tempFile : tempFiles) {
                raf.seek(start);
                fis = new FileInputStream(tempFile);
                while ((len = fis.read(buffer)) > 0) {
                    raf.write(buffer, 0, len);
                    start += len;
                }
                Files.delete(tempFile.toPath());
            }
        } finally {
            IOUtils.closeQuietly(raf);
            IOUtils.closeQuietly(fis);
        }
    }

    /**
     * 读取txt里的单行内容
     * @param fileP  文件路径
     */
    public static String readTxtFile(String fileP) {
        try {

            String filePath = String.valueOf(Thread.currentThread().getContextClassLoader().getResource(""))+"../../";	//项目路径
            filePath = filePath.replaceAll("file:/", "");
            filePath = filePath.replaceAll("%20", " ");
            filePath = filePath.trim() + fileP.trim();
            if(filePath.indexOf(":") != 1){
                filePath = File.separator + filePath;
            }
            String encoding = "utf-8";
            File file = new File(filePath);
            if (file.isFile() && file.exists()) { 		// 判断文件是否存在
                InputStreamReader read = new InputStreamReader(
                        new FileInputStream(file), encoding);	// 考虑到编码格式
                BufferedReader bufferedReader = new BufferedReader(read);
                String lineTxt = null;
                while ((lineTxt = bufferedReader.readLine()) != null) {
                    return lineTxt;
                }
                read.close();
            }else{
                System.out.println("找不到指定的文件,查看此路径是否正确:"+filePath);
            }
        } catch (Exception e) {
            System.out.println("读取文件内容出错");
        }
        return "";
    }
}
